﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ImageEffect
{
    public partial class ColorPicker : System.Windows.Forms.UserControl
    {
        private Bitmap bmpHue = new Bitmap(1,1);

        private Color _color = Color.White;
        public System.Drawing.Color Color
        {
            get { return _color; }
            set
            {
                _color = value;
                this.Alpha.Value = _color.A;

                CalculateHSV();
            }
        }

        private float _hue, _sat, _val;
        public float Hue
        {
            get { return _hue; }
            set
            {
                _hue = value;
                CalculateRGB();
            }
        }
        public float Sat
        {
            get { return _sat; }
            set
            {
                _sat = value;
                CalculateRGB();
            }
        }
        public float Val
        {
            get { return _val; }
            set
            {
                _val = value;
                CalculateRGB();
            }
        }

        public ColorPicker(System.Drawing.Color color)
        {
            InitializeComponent();

            this.Color = color;
            
            CreateHueBitmap();
            UpdateControls();
        }

        private void CreateHueBitmap()
        {
            bmpHue = new Bitmap(hueBar.Width, hueBar.Height);

            for (int y = 0; y < bmpHue.Height; y++)
            {
                float pct = (float)y / (float)bmpHue.Height;
                Color c = HSV_to_RGB(pct * 6, 1, 1);

                for (int x = 0; x < bmpHue.Width; x++)
                {
                    bmpHue.SetPixel(x, y, c);
                }
            }

            hueBar.Image = new Bitmap(bmpHue);
        }

        private void FillSatValBox()
        {
            Bitmap bmp = new Bitmap(colorBox.Width, colorBox.Height);

            for (int y = 0; y < bmp.Height; y++)
            {
                float pctY = (float)y / (float)bmp.Height;

                for (int x = 0; x < bmp.Width; x++)
                {
                    float pctX = (float)x / (float)bmp.Width;

                    Color c = HSV_to_RGB(this.Hue, pctX * 1, pctY * 1);
                    bmp.SetPixel(x, y, Color.FromArgb(_color.A, c.R, c.G, c.B));
                }
            }

            colorBox.Image = bmp;
            colorBox.Invalidate();
        }

        Color HSV_to_RGB(float h, float s, float val)
        {
            int v = (int)(val * 255);

            if (h < 0 || h > 6)
                return Color.FromArgb(v, v, v);

            float i = (float)Math.Floor(h);
            float f = h - i;

            if ((((int)i) & 1) == 0)
                f = 1 - f; // if i is even

            int m = (int)(val * (1 - s) * 255);
            int n = (int)(val * (1 - s * f) * 255);
            switch ((int)i)
            {
                case 6:
                case 0: return Color.FromArgb(v, n, m);
                case 1: return Color.FromArgb(n, v, m);
                case 2: return Color.FromArgb(m, v, n);
                case 3: return Color.FromArgb(m, n, v);
                case 4: return Color.FromArgb(n, m, v);
                case 5: return Color.FromArgb(v, m, n);
            }
            return Color.FromArgb(v, v, v);
        }

        private void CalculateRGB()
        {
            Color c = HSV_to_RGB(this.Hue, this.Sat, this.Val);

            _color = Color.FromArgb(_color.A, c.R, c.G, c.B);
        }

        private void CalculateHSV()
        {
	        // RGB are each on [0, 1]. S and V are returned on [0, 1] and H is
	        // returned on [0, 6]. Exception: H is returned UNDEFINED if S==0.
	        float R = (float )this.Color.R / 255,
                  G = (float )this.Color.G / 255,
                  B = (float )this.Color.B / 255,
                  v, x, f;

	        x = Math.Min(R, Math.Min(G, B));
            v = Math.Max(R, Math.Max(G, B));

            int i;
            if (v == x)
            {
                this.Hue = 0;
                this.Sat = 0;
                this.Val = v;

                return;
            }

	        f = (R == x) ? G - B : ((G == x) ? B - R : R - G);
	        i = (R == x) ? 3 : ((G == x) ? 5 : 1);
	
            this._hue = i - f /(v - x);
            this._sat = (v - x)/v;
            this._val = v;
        }

        void UpdateControls()
        {
            // Load the background colors
            hueBar.Image = new Bitmap(bmpHue);
            FillSatValBox();

            // Show our current color
            PlaceCursors();
        }

        void PlaceCursors()
        {
            // Draw a line on the hue bar
            int y = (int)((this.Hue / 6) * (float)hueBar.Height);
            Graphics.FromImage(hueBar.Image).DrawLine(
                new Pen(Color.Black),
                new Point(0, y),
                new Point(hueBar.Image.Width, y)
            );

            // Draw a cursor on the saturation box
            int x = (int)(this.Sat * (float)colorBox.Width);
            y = (int)(this.Val * (float)colorBox.Height);
            Graphics.FromImage(colorBox.Image).FillRectangle(
                new SolidBrush(Color.White),
                new Rectangle(x - 1, y - 1, 2, 1)
            );
            Graphics.FromImage(colorBox.Image).DrawLine(
                new Pen(Color.Black),
                new Point(x - 2, y),
                new Point(x + 2, y)
            );
            Graphics.FromImage(colorBox.Image).DrawLine(
                new Pen(Color.Black),
                new Point(x, y-2),
                new Point(x, y+2)
            );
        }

        private void hueBar_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                float pct = (float)e.Location.Y / (float)hueBar.Height;
                this.Hue = pct * 6.0f;

                UpdateControls();
            }
        }

        private void colorBox_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                this.Sat = (float)e.Location.X / (float)colorBox.Width;
                this.Val = (float)e.Location.Y / (float)colorBox.Height;

                UpdateControls();
            }
        }

        private void colorBox_SizeChanged(object sender, EventArgs e)
        {
            UpdateControls();
        }

        private void Alpha_ValueChanged(object sender, EventArgs e)
        {
            int a = this.Alpha.Value;

            this._color = Color.FromArgb(a, _color.R, _color.G, _color.B);

            UpdateControls();
        }
    }
}
